from pydub import AudioSegment
from Song import Song
import random
import json
import math
import os
import shutil
import codecs
import traceback



class PydubCore:
    def __init__(self, paragraph_filename, paragraph_condition):
        self.paragraph_filename = paragraph_filename
        self.paragraph_condition = paragraph_condition
        self.url_prefix = 'uploads/'
        self.set_all_songs()
        self.load_prj_s()
        self.bgm_1_index=0
        self.loop_index = 0
        self.now_move_i = 0
        self.old_move = 0
        self.random_change = 6#random.randint(3, 6)

    def set_all_songs(self):
        self.all_songs = []
        for x in self.paragraph_filename:
            print('this x url is : ' + self.url_prefix + x)
            song = Song(self.url_prefix + x, x, AudioSegment.from_wav(self.url_prefix + x))
            self.all_songs.append(song)

    def do_exec(self):
        if len(self.find_songs_by_p(5)) < 2:
            res_dict = {'success': False, 'data': '动作指示信号最少传两个'}
            return json.dumps(res_dict)
        self.set_all_songs()
        self.per_build()
        return json.dumps(self.do_create())

    def do_silent(self, t): #用于补齐时长
        return AudioSegment.silent(duration=t * 1000)

    def has_p_condition(self, p): #返回是否存在某个部分的音乐
        return self.paragraph_condition.__contains__(p)

    def find_p_condition(self, p): #返回某个部分的音乐
        return self.paragraph_condition[p]

    def find_song(self, p): #根据所属部分查找音乐
        for x in self.all_songs:
            if p == x.pos:
                return x.pydub_obj
        return AudioSegment.empty()

    def find_correct_pos(self, res, pos): #查找当前部分的某个小节, 返回pydub类
        for m in res:
            if m.y == pos:
                return m.pydub_obj

    def randomP5_and_p7(self, x): #随机将第五段与第七段音乐配组
        _, res5 = self.find_songs_by_p(5)
        if len(res5) == 0:
            return AudioSegment.empty(), AudioSegment.empty()
        if len(res5) - 1 == 0:
            _, res7 = self.find_songs_by_p(7)
            return res5[0].pydub_obj, res7[0].pydub_obj
        if len(res5) == 2 and self.find_p_condition('5') != '3':
            r_int = random.randint(0, res5.__len__() - 1)
            _, res7 = self.find_songs_by_p(7)
            if self.has_p_condition('52'):
                return self.find_correct_pos(res5, int(self.p5_way[x % 2]) - 1), self.find_correct_pos(res7, int(
                    self.p5_way[x % 2]) - 1)
            return self.find_correct_pos(res5, r_int), self.find_correct_pos(res7, r_int)
        if self.find_p_condition('5') == '1':
            r_res = random.randint(0, 10)
            _, res7 = self.find_songs_by_p(7)
            if r_res <= 4:
                return self.find_correct_pos(res5, 0), self.find_correct_pos(res7, 0)
            if r_res > 4 and r_res <= 8:
                return self.find_correct_pos(res5, 1), self.find_correct_pos(res7, 1)
            else:
                t = random.randint(2, res5.__len__() - 1)
                return self.find_correct_pos(res5, t), self.find_correct_pos(res7, t)
        elif self.find_p_condition('5') == '2':
            _, res7 = self.find_songs_by_p(7)
            t = random.randint(0, res5.__len__() - 1)
            return self.find_correct_pos(res5, t), self.find_correct_pos(res7, t)
        else:
            if not self.has_p_condition('51'):
                c_5_1 = 0
            else:
                c_5_1 = self.find_p_condition('51')
            _, res7 = self.find_songs_by_p(7)
            if math.ceil(float(c_5_1)) != 0:
                t = int(self.p5_way[int(x % len(self.p5_way))]) - 1
                sub_t = float(c_5_1) * 1000
                res5_obj = self.find_correct_pos(res5, t)
                res7_obj = self.find_correct_pos(res7, t)
                return res5_obj[:sub_t], res7_obj

            else:
                if x > len(self.p5_way) - 1:
                    x = x % len(self.p5_way)
                return self.find_correct_pos(res5, int(self.p5_way[x]) - 1), self.find_correct_pos(res7, int(self.p5_way[x]) - 1)

    def find_songs_by_p(self, p): #查找所有指定部分的音乐
        res = []
        res_song = []
        for x in self.all_songs:
            if x.x == p:
                res_song.append(x)
                res_song.sort(key=self.comp)
        for x in res_song:
            res.append(x.pydub_obj)
        return res, res_song

    def comp(self, key):
        return key.y

    def per_build(self):
        if self.has_p_condition('52'):
            self.p5_way = (self.find_p_condition('52')).split(' ')
        for x in range(12):
            _, res_song = self.find_songs_by_p(x)
            if len(res_song) > 0:
                res_song.sort(key=self.comp)
            for i in range(len(res_song)):
                res_song[i].y = i

    def correct_remix_bg2(self, song_res, song_bg_2, bgm_index, loop_index, bg_2_index):
        _, song_p_2 = self.find_songs_by_p(2)
        _, song_p_11 = self.find_songs_by_p(11)
        _, tmp_all_p5 = self.find_songs_by_p(5)
        song_res_array = []
        res = AudioSegment.empty()
        if song_res.duration_seconds <= song_bg_2.duration_seconds:
            x = math.floor(song_bg_2.duration_seconds / song_res.duration_seconds)
            test_song_bg = song_bg_2.duration_seconds
            test_song_res = song_res.duration_seconds
            if x == 1:
                loop_index = loop_index+1
                song_res, loop_index = self.do_loop(res, song_p_2, song_p_11, tmp_all_p5, bgm_index, loop_index, True, bg_2_index)
                res = song_res.overlay(song_bg_2, loop=True)
            else:
                for i in range(x):
                    loop_index = loop_index + 1
                    song_res_value, loop_index = self.do_loop(res, song_p_2, song_p_11, tmp_all_p5, bgm_index, loop_index, True, bg_2_index)
                    song_res_array.append(song_res_value)
                res = song_res
                for songs in song_res_array:
                    res = res + songs.overlay(song_bg_2, loop=True)

            return res, loop_index
        else:
            res = song_res.overlay(song_bg_2, loop=True)
            return res, loop_index

    def correct_remix(self, song_res, song_bg):
        return song_res.overlay(song_bg, loop=True)

    def do_loop(self, final_res, song_p_2, song_p_11, tmp_all_p5, bgm_index, x, from_remix_bg2, bg_2_index):
        p5_song_1 = self.find_correct_pos(tmp_all_p5, 0) #第五部分第一小节
        p5_song_2 = self.find_correct_pos(tmp_all_p5, 1) #第五部分第二小节

        RES = AudioSegment.empty()
        # last_add_p7 = True
        empty_2s = self.do_silent(2)
        if not from_remix_bg2:
            #修改前: bg_2 = empty_2s + self.find_correct_pos(song_p_2, x)
            bg_2 = empty_2s + self.find_correct_pos(song_p_2, bg_2_index)
        tmp = self.do_silent(
            random.randint(int((self.paragraph_condition['4'])[0]), int((self.paragraph_condition['4'])[1])))
        # 添加动作指示信号
        tmp_p5, tmp_p7 = self.randomP5_and_p7(self.loop_index)
        RES = RES + tmp_p5
        # 听到信号后做动作的时间
        RES = RES + self.do_silent(float((self.paragraph_condition['6'])[0]))
        # 加入持续做动作音

        p7_condition = self.find_p_condition('7')
        p7_2_condition = 0
        if self.has_p_condition('72'):
            p7_2_condition = self.find_p_condition('72')
        p7_obj = tmp_p7
        if int(p7_2_condition) != 0:
            p7_obj = p7_obj[:int(p7_2_condition) * 1000]
        if p7_condition == 'repeat':
            if self.loop_index == 0 and not from_remix_bg2:
                RES = RES + self.find_song('8-01')
            elif p5_song_1 == tmp_p5 or p5_song_2 == tmp_p5:
                if tmp_p5 == self.last_loop_p5:
                    RES = RES + p7_obj
                else:
                    RES = RES + self.find_song('8-01')
            else:
                if tmp_p5 == self.last_loop_p5:
                    RES = RES + p7_obj
                else:
                    RES = RES + self.find_song('8-01')
            self.last_loop_p5 = tmp_p5
        elif p7_condition == 'differ':
            if self.loop_index == 0 and not from_remix_bg2:
                RES = RES + self.find_song('8-01')
            elif p5_song_1 == tmp_p5 or p5_song_2 == tmp_p5:
                if tmp_p5 != self.last_loop_p5:
                    RES = RES + p7_obj
                else:
                    RES = RES + self.find_song('8-01')
            else:
                if tmp_p5 != self.last_loop_p5:
                    RES = RES + p7_obj
                else:
                    RES = RES + self.find_song('8-01')
            self.last_loop_p5 = tmp_p5
        else:
            RES = RES + p7_obj
        # # 加入不触发动作音
        # if not last_add_p7:
        #     RES = RES + self.find_song('8-01')
        # 动作结束音
        RES = RES + self.find_song('10-01')
        # 交换动作信号(如果交换动勾选则随机交换否则不交换)p
        if self.find_p_condition('11'):
            # 测试  random.randint(3, 6)
            if self.loop_index % self.random_change == 0 and len(song_p_11) >= 1 and self.loop_index != 0:
                self.old_move = self.now_move_i
                if self.now_move_i == 0:
                    self.now_move_i = 1
                    self.bgm_1_index = 0 if self.bgm_1_index == 1 else 1
                else:
                    self.now_move_i = 0
                    self.bgm_1_index = 0 if self.bgm_1_index == 1 else 1
                # self.loop_index = self.loop_index % len(self.p5_way) if self.p5_way else self.loop_index

        # 插入空闲时间 (此处有问题)
        RES = RES + tmp
        # 统计背景音时长
        bg_tmp, _ = self.find_songs_by_p(1)
        bg_1 = bg_tmp[self.bgm_1_index]
        if not self.find_p_condition('11'):
            bg_1 = bg_tmp[0]

        #如果 不是第一段 背景音乐1
        if bg_2_index != 0:
            if not from_remix_bg2:
                RES = self.correct_remix(RES, bg_1)
                self.loop_index += 1
                RES, x = self.correct_remix_bg2(RES, bg_2, self.bgm_1_index, self.loop_index, bg_2_index)
            else:
                if self.old_move != self.now_move_i:
                    self.old_move = self.now_move_i
                    switch_move = song_p_11[self.now_move_i]
                    bg_1 = bg_tmp[self.bgm_1_index]
                    RES = switch_move.pydub_obj + tmp + self.correct_remix(RES, bg_1) if from_remix_bg2 else RES + switch_move.pydub_obj if bg_2_index < len(song_p_2) - 1 else RES
                else:
                    RES = self.correct_remix(RES, bg_1)
        # 如果是第一循环，则添加宣布开始音
        elif not from_remix_bg2 and bg_2_index == 0 and self.loop_index == 0:
            tmp_res_x = tmp + RES
            # 添加延迟（间隔时间）后，与背景音1混合
            tmp_res_x = self.correct_remix(tmp_res_x, bg_tmp[0])
            # 与背景音2混合
            if not from_remix_bg2:
                self.loop_index += 1
                tmp_res_x, x = self.correct_remix_bg2(tmp_res_x, bg_2, self.bgm_1_index, self.loop_index, bg_2_index)
                RES = self.find_song('3-01') + tmp_res_x
        # 如果 是第一段 背景音乐 且是递归调用
        elif not from_remix_bg2 and bg_2_index != 0:
            self.loop_index += 1
        else:
            if self.old_move != self.now_move_i:
                self.old_move = self.now_move_i
                switch_move = song_p_11[self.now_move_i]
                # RES = self.correct_remix(RES, bg_1)
                bg_1 = bg_tmp[self.bgm_1_index]
                RES = switch_move.pydub_obj + tmp + self.correct_remix(RES,bg_1) if from_remix_bg2 else RES + switch_move.pydub_obj if bg_2_index < len(song_p_2) - 1 else RES
            else:
                RES = self.correct_remix(RES, bg_1)

        final_res = final_res + RES
        if from_remix_bg2:
            self.loop_index += 1
        return final_res, x

    def do_create(self):
        try:
            final_res = AudioSegment.empty()
            _, song_p_2 = self.find_songs_by_p(2)
            self.last_loop_p5 = AudioSegment.empty()
            _, song_p_11 = self.find_songs_by_p(11)
            self.now_move_i = 0
            _, tmp_all_p5 = self.find_songs_by_p(5)
            z = 0
            for x in range(len(song_p_2)):
                final_res, z = self.do_loop(final_res, song_p_2, song_p_11, tmp_all_p5, 0, z, False, x)
            final_res = final_res + self.find_song('9-01')
        except Exception as e:
            res_dict = {'success': False, 'data': traceback.format_exc() + '\n提示：如果出现keyerror类似错误，表示有一项没有上传'}
            return res_dict
        else:
            final_res.export("uploads/res.mp3", format="mp3")
            res_dict = {'success': 'true', 'url': 'uploads/res.mp3'}
            self.bgm_1_index = 0
            self.loop_index = 0
            self.random_change = 6#random.randint(3, 6)
            self.now_move_i = 0
            self.old_move = 0
            return res_dict

    def load_prj_s(self):
        list = os.listdir('.')
        self.loadable_list = []
        for k in list:
            if k.startswith('saved_'):
                if os.path.isdir('./' + k):
                    is_ex_str = './' + k + '/' + (k.split('_'))[1] + '.prj'
                    if os.path.exists(is_ex_str):
                        self.loadable_list.append((k.split('_'))[1])

    def load_prj(self, prj_name):
        dir_str = './saved_' + prj_name
        shutil.rmtree('uploads/')
        shutil.copytree(dir_str,
                        './uploads')
        f = codecs.open('uploads/' + prj_name + '.prj', 'r', 'utf-8')
        s = f.readlines()
        f.close()
        self.paragraph_filename = eval(s[0].encode('utf-8'))
        self.paragraph_condition = eval(s[1].encode('utf-8'))
        self.set_all_songs()
        self.per_build()

    def save_prj(self, prj_name):
        f = codecs.open('uploads/' + prj_name + '.prj', 'w', 'utf-8')
        x1 = str(self.paragraph_condition)
        x2 = str(self.paragraph_filename)
        f.writelines(x2)
        f.writelines('\n')
        f.writelines(x1)
        f.close()
        shutil.copytree('./uploads',
                        'saved_' + prj_name)
        self.loadable_list.append(prj_name)
        shutil.rmtree('uploads/')
        os.mkdir('uploads/')

    def delete_prj(self, prj_name):
        shutil.rmtree('saved_' + prj_name)

